package ee.ut.aa.neuraltic.genetic;

import java.util.Collections;
import java.util.List;
import java.util.Random;

import org.apache.log4j.Logger;

import ee.ut.aa.neuraltic.neural.Layer;
import ee.ut.aa.neuraltic.neural.Network;
import ee.ut.aa.neuraltic.neural.Neuron;
import ee.ut.aa.neuraltic.neural.Synaps;
import ee.ut.aa.neuraltic.neural.TicNetwork;

public class Knowledge {

	private static Logger log = Logger.getLogger( Knowledge.class );

	Random random = new Random();

	public Network chooseMate( List<Network> population ) {

		return population.get( random.nextInt( Brain.POPULATION ) );
	}

	public Network mate( Network father, Network mother ) {

		Network child = new TicNetwork();

		int nrOfLayers = father.getLayers().size();

		// Should not be zero
		int split = random.nextInt( nrOfLayers - 1 ) + 1;
		
		//log.debug( "Mating father and mother with split=" + split );

		for( int i = 0; i < nrOfLayers; i++ ) {
			
			Layer pLayer; // Parent
			
			if( i < split )
				pLayer = father.getLayers().get( i );
			else
				pLayer = mother.getLayers().get( i );
			
			Layer cLayer = child.getLayers().get( i );
			
			int nrOfNeurons = pLayer.getNeurons().size();
			
			for( int j = 0; j < nrOfNeurons; j++ ) {
				Neuron pNeuron = pLayer.getNeurons().get( j );
				Neuron cNeuron = cLayer.getNeurons().get( j );
				
				int nrOfSynapses = pNeuron.getSynapses().size();
				
				for( int k = 0; k < nrOfSynapses; k++ ) {
					Synaps pSynaps = pNeuron.getSynapses().get( k );
					Synaps cSynaps = cNeuron.getSynapses().get( k );
					
					// That's where the magic happens ("DNA transfer").
					cSynaps.setWeight( pSynaps.getWeight() );
					
				}
			}
		}

		return child;
	}

	public void mutate( Network child ) {

		log.debug( "Mutating a child." );

		List<Layer> layers = child.getLayers();

		for( Layer layer : layers )
			for( Neuron neuron : layer.getNeurons() )
				for( Synaps synaps : neuron.getSynapses() )
					if( random.nextDouble() > 0.9 )
						synaps.setWeight( Math.random() * 2 - 1);
	}

	public List<Network> naturalSelection( List<Network> newPop ) {

		log.debug( "Starting natural selection." );

		String debug = "";
		if( log.isDebugEnabled() ) {
			for( Network network : newPop ) {
				debug += network.getValue() + ";";
			}
			log.debug( "Values of networks( " + newPop.size() + "): " + debug );
		}

		Collections.sort( newPop, new NetworkComparator() );

		newPop = newPop.subList( 0, Brain.POPULATION );

		debug = "";
		if( log.isDebugEnabled() ) {
			for( Network network : newPop ) {
				debug += network.getValue() + ";";
			}
			log.debug( "Sorted newpop of networks: " + debug );
		}

		for( Network network : newPop ) {
			network.setValue( 0 );
		}

		debug = "";
		if( log.isDebugEnabled() ) {
			for( Network network : newPop ) {
				debug += network.getValue() + ";";
			}
			log.debug( "Reset values of networks: " + debug );
		}

		return newPop;
	}
}
